4

1.什么是闭包?闭包有啥特性以及存在什么问题?

概念:闭包是指有权访问另一个函数作用域中的变量的函数。下面的outer就形成了一个闭包:
function outer(){
    const name='nagi';
    return function inner(){
        console.log(name);
    }
 }
let p=outer();
}
let p=outer();

一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局执行环境中的变量对象,但闭包有所不同。
当outer()执行完后,因为inner函数的作用域链在引用outer的活动对象,所以它并不会被销毁,而是仍然留在内存中,
除非inner函数也销毁它的活动对象才会被销毁。比如使p=null;

特性: 由上面的代码可以得出以下几个特性:

a. 函数嵌套函数,作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
b. 函数内部可以引用外部的参数和变量。
c. 一个闭包就是当一个函数返回时,一个没有释放资源的栈区,所以参数和变量不会被垃圾回收机制回收。

优点:

 a. 减少全局变量的污染
 b. 可以有私有变量的存在
function counter(){
    let n=0;
    function add(){
        n++;
       console.log(n);
    }
    return {n:n, add:add}
}
const c1=counter();
const c2=counter(); // 它和c1分别存入了不同的堆内存中
c1.add(); // 1
c1.add(); // 2
c1.n;     // 0 // 此处的n是基本类型,除非重新赋值,否则不会变!
c2.add(); // 1 c1和c2互不干涉,都有自己的新的作用域链和新的私有变量
注意!!父函数每次调用会产生新的闭包

缺点(问题)

a.常驻内存,增加内存使用量。
b. 使用不当会很容易造成内存泄露。
另:闭包的多种写法可以参照这里:JavaScript闭包(closure)

2.js中的垃圾回收机制

 原理:js中有自动回收管理内存机制,它的原理是会定期(周期性地)找出那些不再继续使用的变量,然后释放其占用的内存。

内存管理:

1. 分配内存(不管是基本类型还是引用类型)
2. 使用内存(对变量,函数等读取或写操作)
3. 释放内存(使用垃圾回收机制回收内存)

回收方式

1.引用计数

**含义:**跟踪记录每个值被引用的次数。
**工作机制:**
    当声明一个变量并将一个引用类型值赋给它时,则这个值的引用次数就是1;
    如果同一个值又被赋给另一个变量,则该值的引用次数加1;
    相反,如果包含对这个值引用的变量又被赋了其他值,则这个值的引用次数减1;
    当这个值的引用次数为0时,则说明没有办法再访问这个值了;
    这样,当垃圾收集器下次再运行时,便会释放这种引用次数为0的值所占的内存。

问题:

这种方式有一个严重的问题,即“循环引用”。
意思是对象A中包含一个指向对象B的指针,而对象B也包含一个指向对象A的指针,
这样当函数执行完毕后,因为其引用也就永远不会为0,所以对象A和B将继续存在,
如果对象所在函数被重复调用,就会导致大量内存得不到回收。
 function problem(){
     let objA=new Object();
     let objB=new Object();

     objA.someOtherObj=objB;
     objB.antherObj=objA;
 }

2.标记清除(常用方式)

原理:垃圾回收器会在运行时给存储在内存中的所有变量加一个标记。
(当变量进入环境时,就交这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。)

然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。

而在这些完成之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。

最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。


夕凪
258 声望11 粉丝